home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 2.iso
/
STUTTGART
/
LANG
/
C
/
LIB
/
DESK
/
CORE
/
Desk
/
h_doc
/
JumpRaw
< prev
next >
Wrap
Text File
|
1996-08-16
|
11KB
|
462 lines
/*
#### # # # #
# # # # # The FreeWare C library for
# # ## ### # # # # ### RISC OS machines
# # # # # # # # # # # ___________________________________
# # #### ### ## # # # #
# # # # # # # # # # Please refer to the accompanying
#### ### #### # # ##### # ### documentation for conditions of use
________________________________________________________________________
File: Jump.h
Author: Copyright © 1995 Julian Smith
Version: 1.00 (26 Jan 1996)
Purpose: Provides try..throw..catch system without pulling in DeskLib's
Error2 header etc (unless compiling for SDLS.
*/
#ifndef __Desk_JumpRaw_h
#define __Desk_JumpRaw_h
#ifdef __cplusplus
extern "C" {
#endif
#include <setjmp.h>
#ifndef __Desk_Debug_h
#include "Desk.Debug.h"
#endif
/*
This header defines two sets of macros, Desk_Jump_* and Desk_JumpAuto_* . There
are rather a lot of macros, so that you can choose to use them in the
way you want, rather than be forced to write things in a particular way.
Probably the most useful are the three Desk_JumpAuto_Try, Desk_JumpAuto_Catch and
Desk_JumpAuto_EndCatch macros, or the Desk_JumpAuto_TryCatch macro.
Desk_Jump_ macros
------------
The macros Desk_Jump_* are for SDLS-happy set/longjmp-ing. They behave like
setjmp / longjmp etc in normal compilation, but do a few extra things
when compilation is for a SDLS, because the SDLS needs to know about
long-jmping in order to keep an internal stack up-to-date.
See Desk_Jump_SetJmp for more information.
Desk_JumpAuto_ macros
----------------
The macros Desk_JumpAuto_* can be used in a similar way, except that they
also automatically define and use local variables to effectively
maintain a stack of jmp_buf's. This allows a function to call
'Desk_JumpAuto_Throw( val) instead of Desk_Jump_LongJmp( buf, val), ie without
having been passed a jmp_buf explicitly.
This means you don't have to pass a (jmp_buf *) to every function which
might want to call 'longjmp'.
To make things even simpler, you can use the Desk_JumpAuto_Try,
Desk_JumpAuto_Catch, Desk_JumpAuto_EndCatch or Desk_JumpAuto_TryCatch macros.
*/
/* Desk_Jump_* */
typedef struct {
jmp_buf jmpbuf;
#ifdef Desk__using_SDLS
int Desk_jump__sdls_stackptr;
#endif
}
Desk_jump_buf;
/*
This is identical to the normal jmp_buf when compilation is for static
linking. With SDLS, an extra field is present which is needed by the
SDLS.
*/
#ifdef Desk__using_SDLS
#include "Desk.Core.h"
#define Desk_Jump_SetJmp( buf) ((buf).Desk_jump__sdls_stackptr=_dll_setjmp(), setjmp( (buf).jmpbuf))
/*
int Desk_Jump_SetJmp( Desk_jump_buf buf);
*/
#define Desk_Jump_ReceiveLongJmp( buf) _dll_longjmped( (buf).Desk_jump__sdls_stackptr)
/*
void Desk_Jump_ReceiveLongJmp( Desk_jump_buf buf);
*/
#else
#define Desk_Jump_SetJmp( buf) setjmp( (buf).jmpbuf)
/*
int Desk_Jump_SetJmp( Desk_jump_buf buf);
SDLS-compatible version of 'setjmp'.
Instead of:
jmp_buf buf;
void Bar( void) { if (error) longjmp( buf, 1); }
void Foo( void)
{
if ( !setjmp( buf)) { ... Bar(); ... }
else |* Handler error *| }
}
do:
Desk_jump_buf buf;
void Bar( void) { if (error) Desk_Jump_LongJmp( buf, 1); }
void Foo( void)
{
if ( !Desk_Jump_SetJmp( buf)) { ... Bar(); ... }
else {
Desk_Jump_ReceiveLongJmp( buf);
|* Handle error *|
}
}
Ie. Desk_Jump_SetJmp and Desk_Jump_LongJmp are drop-in replacements for setjmp
and longjmp. Desk_Jump_ReceiveLongJmp() should be called immediately after a
longjmp happens, and Desk_jump_buf should be used instead of jmp_buf, to
define a jmp_buf.
*/
#define Desk_Jump_ReceiveLongJmp( buf)
/*
Macro to call when a longjmp is received, for compatibility with SDLS.
See Desk_Jump_SetJmp.
*/
#endif
#define Desk_Jump_LongJmp( buf, val) longjmp( (buf).jmpbuf, val)
/*
void Desk_Jump_LongJmp( Desk_jump_buf buf, int val);
SDLS-compatible version of 'setjmp'.
Actually, as you can see, this macro is identical in SDLS and non-SDLS
builds. It is included so that one can use 'Desk_Jump_' macros for
everything.
See Desk_Jump_SetJmp.
*/
/* Desk_JumpAuto_* */
typedef struct Desk_jumpauto_buf {
Desk_jump_buf jumpbuf;
struct Desk_jumpauto_buf* previous;
}
Desk_jumpauto_buf;
/*
This contains all information needed to allow the JumpAuto system to
keep track of a stack of Desk_jump_buf's.
*/
#ifdef Desk__using_SDLS
extern Desk_jumpauto_buf** Desk_Jump__Ref_autonewestbuf( void);
#endif
#if defined( Desk__using_SDLS) && !defined( Desk__making_Jump)
#define Desk_jumpauto_newestbuf (*Desk_Jump__Ref_autonewestbuf())
#else
extern Desk_jumpauto_buf *Desk_jumpauto_newestbuf;
/*
Always points to the most recent Desk_jumpauto_buf. Is NULL initialy.
*/
#endif
#define Desk_JumpAuto__EscapeFromNestedTryUsingReturnOrBreak() \
(Desk_jumpauto_newestbuf!=&Desk_jumpauto__localbuf)
/*
A macro that detects list corruption (probably caused by breaking or
returning from a try block).
*/
#define Desk_JumpAuto__Push( jumpautobuf) \
(jumpautobuf).previous = Desk_jumpauto_newestbuf; \
Desk_jumpauto_newestbuf = &(jumpautobuf)
/*
This makes subsequent Desk_JumpAuto_LongJmp's go to 'jumpautobuf', and stores
where they were originally going, so that this place can be restored
later.
*/
#define Desk_JumpAuto__Pop( jumpautobuf) \
Desk_jumpauto_newestbuf = (jumpautobuf).previous
/*
Makes subsequent Desk_JumpAuto_LongJmp's go to whatever was the previous
Desk_JumpAuto_SetJmp point (usually this will be in a parent function of the
present function).
*/
#define Desk_JumpAuto_Try \
{ \
Desk_jumpauto_buf Desk_jumpauto__localbuf; \
volatile int Desk_jumpauto_val; \
Desk_JumpAuto__Push( Desk_jumpauto__localbuf); \
Desk_jumpauto_val = Desk_Jump_SetJmp( Desk_jumpauto__localbuf.jumpbuf); \
/*Desk_Debug_PrintMemory( Desk_jumpauto_newestbuf, sizeof( Desk_jump_buf));*/ \
if (!Desk_jumpauto_val) {
/*
See Desk_JumpAuto_TryCatch
*/
#define Desk_JumpAuto_Catch \
/* Clean up after try code has finished succesfully: */ \
/*Desk_Debug_Assert( !Desk_JumpAuto__EscapeFromNestedTryUsingReturnOrBreak());*/ \
Desk_JumpAuto__Pop( Desk_jumpauto__localbuf); \
} \
else { \
/* Clean up after a longjmp: */ \
Desk_JumpAuto__Pop( Desk_jumpauto__localbuf); \
Desk_Jump_ReceiveLongJmp( Desk_jumpauto__localbuf.jumpbuf);
/*
See Desk_JumpAuto_TryCatch
*/
#define Desk_JumpAuto_EndCatch \
} \
}
/*
See Desk_JumpAuto_TryCatch
*/
#define Desk_JumpAuto_Throw( val) Desk_Jump_LongJmp( Desk_jumpauto_newestbuf->jumpbuf, val)
/*
Behaves like longjmp, jmping to the most recent Desk_JumpAuto_SetJmp place.
'val' could be a pointer to an error structure such as an Desk_error2_block
(cast to an int). Note that this structure must not be on the stack.
It is up to the receiving code to call Desk_JumpAutp__Pop, so that subsequent
Desk_JumpAuto_Throw's go somewhere sensible.
*/
#define Desk_JumpAuto_TryCatch( trycode, catchcode) \
Desk_JumpAuto_Try { trycode } \
Desk_JumpAuto_Catch { catchcode } \
Desk_JumpAuto_EndCatch
/*
Purpose
-------
This macro, along with Desk_JumpAuto_Throw, allows you to write pseudo
try...catch code without worrying about the other Jump* macros.
Acorn's cc will warn about 'more then 10 lines of macro argument' if
your 'trycode' and 'catchcode' have many statements.
Example code
------------
void Foo( void)
{
if (...) Desk_JumpAuto_Throw( 2);
}
void Bar( void)
{
if (...) Desk_JumpAuto_Throw( 1);
}
void Somefunction( void)
{
Desk_JumpAuto_TryCatch(
Foo();
Bar(); [DO NOT 'return' or 'break' out of this block]
,
printf( "Foo() or Bar() failed with longjmp( %i)...\n", Desk_jumpauto_val);
)
}
An alternative way of doing the same thing is to use the Desk_JumpAuto_Try,
Desk_JumpAuto_Catch and Desk_JumpAuto_EndCatch macros individually:
Desk_JumpAuto_Try {
Foo();
Bar(); [DO NOT 'return' or 'break' out of this block]
}
Desk_JumpAuto_Catch {
printf( "Foo() or Bar() failed with longjmp( %i)...\n", Desk_jumpauto_val);
}
Desk_JumpAuto_EndCatch
The value passed to 'Desk_JumpAuto_Throw' is available to 'catchcode' as the
local int 'Desk_jumpauto_val'. When Desk_JumpAuto_Error2Handler is used,
Desk_jumpauto_val will be the Desk_error2_block* (cast into an integer).
This means that, if you are using the Error2 system with
Desk_JumpAuto_Error2Handler as the error handler, you could do:
Desk_JumpAuto_TryCatch(
...
,
printf( "Error occurred, address of Desk_error2_block is %p\n",
(Desk_error2_block*) Desk_jumpauto_val
)
)
Notes
=====
'break' and 'return' with Desk_JumpAuto_*
------------------------------------
You should *never* leave the 'try' code with a break, return, goto,
longjmp or similar.
In fact, control should only leave a try block in one of two ways: By
reaching the end of the try block and 'falling through', or by an
exception being thrown.
The reason for this restriction is that the JumpAuto system needs to be
told to remove a jmp_buf from its list of jmp_bufs when a 'try' block is
exited, and this is only done by the 'JumpAuto_Catch' macro. If you
accidently leave a 'try' block with a 'return' or similar, subsequent
calls to Desk_JumpAuto_Throw will cause horrible crashes - the system
will jump to a non-existant jmp_buf.
Terminating ';'
---------------
When using the Desk_JumpAuto_TryCatch macro, you shouldn't use a terminating
';'.
This makes use of Desk_JumpAuto_TryCatch look rather un C-like. It would be
possible to use a 'do ... while (0)' construct in the Desk_JumpAuto_TryCatch
macro (as used in Desk_JumpAuto_ReturnValue, for eg), but this doesn't work
too well if 'catchcode' is exited with a call to 'break' (using break in
this way in 'trycode' is not allowed anyway).
An alternative way of accepting a terminating ';' is to use 'if (1)
{...} else', but this will create really bizarre and confusing errors if
the ';' is forgoton.
Compiler errors
---------------
Because the Desk_JumpAuto_ macros are not part of the language, the compiler
cannot check the syntax. If you misuse them you will generally get some
weird errors.
Automatic variables
-------------------
Unless they are declared as 'volatile', automatic variables that are
modified within the try block will have an undefined value after
the try block if a longjmp is made.
For example:
{
int i = 1; // automatic variable
Desk_JumpAuto_Try
{
i = 2; // modified in try block
if (error) Desk_JumpAuto_Throw( 3);
}
Desk_JumpAuto_Catch
{
'i' is undefined here
}
Desk_JumpAuto_EndCatch
i is undefined here if a longjmp was made.
}
so do:
{
char volatile *s = NULL; // automatic variable
Desk_JumpAuto_Try
{
s = ...; // modified in try block
if (error) Desk_JumpAuto_Throw( 3);
}
Desk_JumpAuto_Catch
{
's' is defined here
}
Desk_JumpAuto_EndCatch
i is defined here even if a longjmp was made.
}
*/
#ifdef __cplusplus
}
#endif
#endif